--[[
	Copyright (C) Udorn (Blackhand)
	
	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.	
--]]

--[[
	Gathers statistics by hooking auction functions.
--]]
vendor.Gatherer = vendor.Vendor:NewModule("Gatherer", "AceHook-3.0", "AceEvent-3.0")
local L = vendor.Locale.GetInstance()
local log = vendor.Debug:new("Gatherer")

local TIME_LEFT = {1800, 7200, 43200, 172800}

--[[
	Sets the given value in the profile.
--]]
local function _SetValue(info, value)
	self.db.profile[info.arg] = value
end

--[[
	Returns the given value from the profile.
--]]
local function _GetValue(info)
	return self.db.profile[info.arg]
end

--[[
	Unpacks the information contained in the auction data represantation.
	itemLinkKey, count, minBid, buyout, name, quality, level, minIncrement, bidAmount, 
		highBidder, owner, saleStatus, time, timeLeft
--]]
local function _Unpack(data)
	local part1, part2, part3 = strsplit("|", data)
	local itemLinkKey, count, minBid, buyout = strsplit(";", part1)
	local time, timeLeft = strsplit(";", part2)
	local name, quality, level, minIncrement, bidAmount, highBidder, owner, saleStatus = strsplit(";", part3)
	return itemLinkKey, tonumber(count), tonumber(minBid), tonumber(buyout), name, quality, 
		tonumber(level), tonumber(minIncrement), tonumber(bidAmount), highBidder, owner, saleStatus, tonumber(time), tonumber(timeLeft or 4)
end

--[[
	Starts a new scan.
--]]
local function _StartScan(self, name, minLevel, maxLevel, invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex, getAll)
	log:Debug("_StartScan minLevel: %d", minLevel or 777)
	if (self.scan) then
		wipe(self.scan.index)
	else
		self.scan = {}
		self.scan.index = {}
	end
	self.scan.name = name
	self.scan.minLevel = minLevel
	self.scan.maxLevel = maxLevel
	self.scan.classIndex = classIndex
	if (classIndex) then
		self.scan.class = select(classIndex, GetAuctionItemClasses())
	end
	self.scan.subclassIndex = subclassIndex
	if (classIndex and subclassIndex) then
		self.scan.subclass = select(subclassIndex, GetAuctionItemSubClasses(classIndex))
	end
	self.scan.invTypeIndex = invTypeIndex
	if (invTypeIndex and classIndex and subclassIndex) then
		self.scan.invType = select(invTypeIndex, GetAuctionInvTypes(classIndex, subclassIndex))
	end
	self.scan.isUsable = isUsable
	self.scan.qualityIndex = qualityIndex
	self.scan.getAll = getAll
	self.scan.length = 0
	self.scan.curPage = 0
	self.scan.neutralAh = vendor.AuctionHouse:IsNeutral()
end

--[[
	Checks whether a new scan has started.
--]]
local function _CheckScan(self, name, minLevel, maxLevel, invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex, getAll)
	if (not self.scan) then
		log:Debug("_CheckScan start missing scan")
		_StartScan(self, name, minLevel, maxLevel, invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex, getAll)
	else
		local scan = self.scan
		if (scan.name ~= name or scan.minLevel ~= minLevel or scan.maxLevel ~= maxLevel or
			scan.invTypeIndex ~= invTypeIndex or scan.classIndex ~= classIndex or
		scan.subclassIndex ~= subclassIndex or scan.isUsable ~= isUsable or scan.qualityIndex ~= qualityIndex) then
			log:Debug("_CheckScan restart scan scan.name [%s] name [%s]", scan.name, name)
			_StartScan(self, name, minLevel, maxLevel, invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex, getAll)
		else
			self.scan.curPage = page
		end
	end
end

--[[
	Extracts the itemLinkKey from the given data line.
--]]
local function _ExtractItemLinkKey(data)
	-- returns the part before the first ";"
	local idx = string.find(data, ";", 1, true)
	return string.sub(data, 1, idx - 1)
end

--[[
	Returns true, if the given auction matches the current scan.
--]]
local function _MatchesScan(self, scan, auction)
	local data1, _, data3 = strsplit("|", auction)
	local itemLinkKey = _ExtractItemLinkKey(data1)
	local name, _, level = strsplit(";", data3)
	level = tonumber(level)
	local rtn = false
	while (true) do
		if (scan.name and not (string.sub(name, 1, string.len(scan.name)) == scan.name)) then
--			log:Debug("_MatchesScan exit because of name (scan: %s auctionb: %s", scan.name or "", name or "")
			break
		end
		if (scan.minLevel and level < scan.minLevel) then
--			log:Debug("_MatchesScan exit because of minLevel")
			break
		end
		if (scan.maxLevel and level > 0 and level > scan.maxLevel) then
--			log:Debug("_MatchesScan exit because of maxLevel (scan: %d auction: %d)", scan.maxLevel or 777, level or 777)
			break
		end
		if (scan.invType or scan.class or scan.subclass or scan.isUsable or scan.qualityIndex) then
--			log:Debug("check inv class %s index: %d subclass %s index: %d", scan.class, scan.classIndex or 777, scan.subclass, scan.subclassIndex or 777)
			local itemLink = vendor.Items:GetItemLink(itemLinkKey)
			local itemName, _, itemRarity, _, _, itemType, itemSubType, _,
				itemEquipLoc = GetItemInfo(itemLink) 
			if (not itemName) then
--				log:Debug("_MatchesScan exit because of missing itemName")
				break
			else
				if (scan.qualityIndex and itemRarity < scan.qualityIndex) then
--					log:Debug("_MatchesScan exit because of quality")
					break
				end
--				log:Debug("invTypeIndex: %d invType: %s", scan.invTypeIndex or 777, scan.invType)
				if (scan.invType and scan.invType ~= itemEquipLoc) then
--					log:Debug("compare invType %s with %s", scan.invType, itemEquipLoc)
					break
				end
				if (scan.class and scan.class ~= itemType) then
--					log:Debug("exist because of class [%s] [%s]", scan.class, itemType)
					break
				end
				if (scan.subclass and scan.subclass ~= itemSubType) then
--					log:Debug("exist because of subclass [%s] [%s]", scan.subclass, itemSubType)
					break
				end
				if (scan.isUsable and not self.canUseEval:IsUsable(itemLink)) then
--					log:Debug("_MatchesScan exit because of usable")
					break
				end
			end
		end
		rtn = true
		break
	end
--	log:Debug("_MatchesScan exit: %s for %s", rtn, name)
	return rtn
end

--[[
	Extracts the auctions from the snapshot corresponding to the current scan.
--]]
local function _ExtractOldAuctions(self, scan, snapshot, auctions)
	log:Debug("_ExtractOldAuctions enter with %d auctions in snapshot", #snapshot)
	debugprofilestart()
	-- move matching auctions to other table
	if (#snapshot > 0) then
    	for i=#snapshot,1,-1 do
    		local data = snapshot[i]
    		if (_MatchesScan(self, scan, data)) then
    			table.insert(auctions, data)
    			table.remove(snapshot, i)
    		end
    	end
    end
    table.sort(auctions)
	log:Debug("_ExtractOldAuctions exit with %d extracted auctions and %d auctions in snapshot in %d millis", #auctions, #snapshot, debugprofilestop())
end

--[[
	Returns true, if the tow auctions match.
--]]
local function _MatchItems(data1, data2)
	local idx1 = string.find(data1, "|", 1, true)	
	local idx2 = string.find(data2, "|", 1, true)
	return string.sub(data1, 1, idx1 - 1) == string.sub(data2, 1, idx2 - 1)
end

--[[
	Remove all auctions in the given table, which are also contained
	in the current scan.
--]]
local function _RemoveDuplicates(self, index, auctions)
	log:Debug("_RemoveDuplicates enter with %d auctions", #auctions)
	debugprofilestart()
	local indexLen = #index
	local auctionsLen = #auctions
	-- FIXME don't use the extra table ??
	local duplicateRemove = {}
	local auctionsOff = 0
	local indexOff = 0
	local auctionData, indexData, data, idx
	while (true) do
		-- get data to compare
		if (not auctionData) then
			auctionsOff = auctionsOff + 1
			if (auctionsOff > auctionsLen) then
				-- not further matches possible
				log:Debug("exit with auctionsOff %d", auctionsOff)
				break
			end
			data = auctions[auctionsOff]
			idx = string.find(data, "|", 1, true)
			auctionData = string.sub(data, 1, idx - 1)
		end
		if (not indexData) then
			indexOff = indexOff + 1
			if (indexOff > indexLen) then
				-- not further matches possible
				log:Debug("exit with indexOff %d", indexOff)
				break
			end
			data = index[indexOff]
			idx = string.find(data, "|", 1, true)
			indexData = string.sub(data, 1, idx - 1)
		end
		-- compare the data
		if (auctionData == indexData) then
			-- we have a match, current auction won't be added
			auctionData = nil
			indexData = nil
		elseif (auctionData < indexData) then
			-- we can't find a match for current auction
			table.insert(duplicateRemove, auctions[auctionsOff])
			auctionData = nil
		else
			-- perhaps the next index entry will match
			indexData = nil
		end	
	end
	log:Debug("_RemoveDuplicates exit with %d auctions in %d millis", #duplicateRemove, debugprofilestop())
	return duplicateRemove
end

--[[
	Reinsert the auction of the given table to the snapshot.
--]]
local function _ReinsertAuctions(self, snapshot, oldAuctions)
	log:Debug("_ReinsertAuctions enter")
	debugprofilestart()
	for i=1,#oldAuctions do
		table.insert(snapshot, oldAuctions[i])
	end
	log:Debug("_ReinsertAuctions exit with %d in snapshot in %d millis", #snapshot or 0, debugprofilestop())
end

--[[
	Insert the auction of the current scan to the snapshot.
--]]
local function _InsertScan(self, index, snapshot)
	log:Debug("_InsertScan enter with %d auction in scan", #index or 0)
	debugprofilestart()
	for i=1,#index do
		table.insert(snapshot, index[i])
	end
	log:Debug("_InsertScan exit with %d in snapshot in %d millis", #snapshot or 0, debugprofilestop())
end

local function _ClearCaches(self)
	log:Debug("_ClearCaches")
	self.currentAuctionCache = wipe(self.currentAuctionCache)
	self.auctionCache = wipe(self.auctionCache)
end

--[[
	Adds the given auctions to the archive.
--]]
local function _ArchiveAuctions(self, auctions, neutralAh)
	debugprofilestart()
	local itemInfo = self.itemInfo
	local itemLinkKey, count, minBid, buyout, data, bidAmount, _
	--table.sort(acutions) -- just to be sure
	local done = 0
	local itemInfo = {}
	for i=1,#auctions do
		itemLinkKey, count, minBid, buyout, _, _, _, _, bidAmount = vendor.Gatherer:Unpack(auctions[i])
		if (not vendor.Items:GetItemInfo(itemLinkKey, itemInfo, neutralAh)) then
			vendor.Items:InitItemInfo(itemInfo)
		end
		if (bidAmount > 0) then
			minBid = bidAmount
		end
		local len = 0
		if (itemInfo.avgBidData) then
			len = itemInfo.avgBidData:len()
		end
		itemInfo.avgBidData = self.bidAvg:AddValue(itemInfo.avgBidData, minBid)
		if (buyout > 0) then
			itemInfo.avgBuyoutData = self.buyoutAvg:AddValue(itemInfo.avgBuyoutData, buyout)
		end
		vendor.Items:PutItemInfo(itemLinkKey, neutralAh, itemInfo)
		done = done + 1
		if (done > 50) then
			--coroutine.yield()
			done = 0
		end
	end
end

--[[
	Moves the outdated auctions in the snapshot to the archive.
--]]
local function _CleanupSnapshot(self, snapshot, neutralAh)
	log:Debug("_CleanupSnapshot enter with %d auctions in snapshot", #snapshot)
	debugprofilestart()
	local removed = 0
	if (#snapshot > 0) then
		local auctions = {} 
		local now = GetTime()
		local itemLinkKey, time, timeLeft, _, prev
		local data1, data2, data3
		for i=#snapshot,1,-1 do
			local data1, data2, data3 = strsplit("|", snapshot[i])
			--itemLinkKey, _, _, _, _, _, _, _, _, _, _, _, time, timeLeft = _Unpack(snapshot[i])
			local time, timeLeft = strsplit(";", data2)
			if (now > (tonumber(time) + TIME_LEFT[tonumber(timeLeft or 4)])) then
				table.insert(auctions, snapshot[i])
				table.remove(snapshot, i)
			end
		end
		removed = #auctions
		_ArchiveAuctions(self, auctions, neutralAh)
	end
	log:Debug("_CleanupSnapshot exit with %d in snapshot removed %d in %d millis", #snapshot, removed, debugprofilestop())
end

--[[
	Starts a task for arching the current scan asynchronously.
--]]
local function _RememberScan(self, complete)
	self.scan.complete = complete
	
	local task = vendor.ArchiveTask:new(self.scan)
	self.scan = nil -- FIXME recycle scan
	vendor.TaskQueue:AddTask(task)

--	local scan = self.scan
--	self.scan = nil
--	self:ArchiveScan(scan)
end

--[[
	Remembers the auctions found in the current scan.
--]]
local function _ArchiveScan(self, scan)
	local removed = 0
	local added = 0
	-- make the scan index a flat list
	local index = {}
	for k,v in pairs(scan.index) do
		table.insert(index, v)
	end
	log:Debug("_ArchiveScan enter with %d auctions in index", #index)
	table.sort(index)
	local extractedAuctions = {}
	local snapshot = self.db.factionrealm.snapshot
	if (scan.neutralAh) then
		snapshot = self.db.realm.snapshot
	end
	log:Debug("_ArchiveScan %d auctions in snapshot", #snapshot)
	_CleanupSnapshot(self, snapshot, neutralAh)
	_ExtractOldAuctions(self, scan, snapshot, extractedAuctions)
	local extracted = _RemoveDuplicates(self, index, extractedAuctions)
	if (not scan.complete) then
		-- readd the old auctions not matched
		_ReinsertAuctions(self, snapshot, extracted)
	else
		-- we are sure, that the auctions in "extracted" are not existing anymore
		_ArchiveAuctions(self, extracted, neutralAh)
		removed = removed + #extracted
	end
	_InsertScan(self, index, snapshot)
	table.sort(snapshot)
	added = #index - #extracted
	--wipe(extracted)
	_ClearCaches(self)
	log:Debug("_ArchiveScan exit removed: %d added: %d snapshot: %d", removed, added, #snapshot)
end

--[[
	Returns the current auction information known about the given item. See GetCurrentAuctionInfo
	for details.
--]]
local function _GetCurrentAuctionInfo(self, itemLinkKey, neutralAh)
	local info = self.currentAuctionCache[itemLinkKey]
	if (not info) then
		info = {}
   		info.numAuctions = 0
   		info.numBuyouts = 0
   		info.avgBid = 0
   		info.avgBuyout = 0
    	local snapshot = self.db.factionrealm.snapshot
    	if (neutralAh) then
    		snapshot = self.db.realm.snapshot
    	end
    	
    	local idx = vendor.Tables.BinarySearch(snapshot, itemLinkKey, _ExtractItemLinkKey)
    	if (idx) then
    		-- select the leftmost entry
    		while (true) do
    			if (idx > 1 and _ExtractItemLinkKey(snapshot[idx - 1]) == itemLinkKey) then
    				idx = idx - 1
    			else
    				break
    			end
    		end
    		-- now calculate the return values
    		local sumBid = 0
    		local sumBuyout = 0
    		local key, count, minBid, buyout, _, bidAmount
    		for i=idx,#snapshot do
    			key, count, minBid, buyout, _, _, _, _, bidAmount = _Unpack(snapshot[i]) 
    			if (key ~= itemLinkKey) then
    				break
    			end
    			info.numAuctions = info.numAuctions + 1
    			if (bidAmount > 0) then
    				minBid = bidAmount
    			end
    			if (count > 1) then
    				minBid = minBid / count
    				buyout = buyout / count
    			end
    			sumBid = sumBid + minBid
    			if (not info.lowerBid or minBid < info.lowerBid) then
    				info.lowerBid = minBid
    			end
    			if (not info.upperBid or minBid > info.upperBid) then
    				info.upperBid = minBid
    			end
    			if (buyout > 0) then
    				info.numBuyouts = info.numBuyouts + 1
        			sumBuyout = sumBuyout + buyout
        			if (not info.lowerBuyout or buyout < info.lowerBuyout) then
        				info.lowerBuyout = buyout
        			end
        			if (not info.upperBuyout or buyout > info.upperBuyout) then
        				info.upperBuyout = buyout
        			end
    			end
    		end
    		-- TODO without mult
    		info.avgBid = math.floor(sumBid / info.numAuctions + 0.5)
    		if (info.numBuyouts > 0) then
    			info.avgBuyout = math.floor(sumBuyout / info.numBuyouts + 0.5)
    		end
    	end
    	self.currentAuctionCache[itemLinkKey] = info
	end
	return info.avgBid, info.avgBuyout, info.lowerBid, info.lowerBuyout, info.upperBid, 
    	info.upperBuyout, info.numAuctions, info.numBuyouts
end

--[[
	Initializes the module.
--]]
function vendor.Gatherer:OnInitialize()
	self.db = vendor.Vendor.db:RegisterNamespace("Gatherer", {
		profile = {
    	},
    factionrealm = {
			snapshot = {},
		},
		_factionrealm = {
			snapshot = {},
		},
		realm = {
			snapshot = {},
		},
	})
	self.itemInfo = {} -- reusing the table
	self.currentAuctionCache = {}
	self.auctionCache = {}
	self.bidAvg = vendor.ApproxAverage:new(30)
	self.buyoutAvg = vendor.ApproxAverage:new(30)
	
	-- for debugging
--	self.db.realm.snapshot = {}
--	self.db.factionrealm.snapshot = {}
end

--[[
	Initializes the module.
--]]
function vendor.Gatherer:OnEnable()
	self.canUseEval = vendor.CanUseEval:new()
	self:RawHook("GetAuctionItemInfo", true)
	self:SecureHook("QueryAuctionItems")
	self:RegisterEvent("AUCTION_HOUSE_CLOSED")
	self:RegisterMessage("AUCTION_MASTER_START_SCAN")
	self:RegisterMessage("AUCTION_MASTER_STOP_SCAN")
end

--[[
	Deinitializes the module.
--]]
function vendor.Gatherer:OnDisable()
	self.canUseEval = nil
	self:Unhook("GetAuctionItemInfo")
	self:Unhook("QueryAuctionItems")
	self:UnregisterEvent("AUCTION_HOUSE_CLOSED")
	self:UnregisterMessage("AUCTION_MASTER_START_SCAN")
	self:UnregisterMessage("AUCTION_MASTER_STOP_SCAN")
end

--[[
	Hooks the retrieval of auction infos, for being able to build statistics.
--]]
function vendor.Gatherer:GetAuctionItemInfo(ahType, index)
	local name, texture, count, quality, canUse, level, minBid,
			minIncrement, buyoutPrice, bidAmount, highBidder, owner,
			saleStatus = self.hooks["GetAuctionItemInfo"](ahType, index)
			
--	local itemLink = GetAuctionItemLink(ahType, index)
--	if (itemLink) then
--    	if (self.canUseEval:IsUsable(itemLink)) then
--    		owner = "T("..(owner or "")..")"
--    	else
--    		owner = "F("..(owner or "")..")"
--    	end
--	end
	if (name and self.scan and ahType == "list") then
		local idx = self.scan.curPage.."-"..index
--			log:Debug("GetAuctionItemInfo new item: %s", name)
		local itemLink = GetAuctionItemLink(ahType, index)
		local itemLinkKey = vendor.Items:GetItemLinkKey(itemLink)
		local timeLeft = GetAuctionItemTimeLeft(ahType, index)
		-- the first part is meant for fast comparisson
		local data = strjoin(";", itemLinkKey, count, minBid, buyoutPrice or "0").."|"..strjoin(";", 
			GetTime(), timeLeft).."|"..strjoin(";", string.lower(name), quality, level or "0", minIncrement or "0", bidAmount or "0", highBidder or "", owner or "", 
			saleStatus or "")
		self.scan.index[idx] = data
--		local batch, count = GetNumAuctionItems(ahType)
--			log:Debug("GetAuctionItemInfo count: %d length: %d", count, self.scan.length)
-- 			currently only managed by Scanner
--			if (self.scan.length >= count) then
--				log:Debug("GetAuctionItemInfo reached maximal number of auctions: %d", count)
--				_RememberScan(self, true)
--    		end
	end
	return name, texture, count, quality, canUse, level, minBid,
			minIncrement, buyoutPrice, bidAmount, highBidder, owner, saleStatus
end

--[[
	Hooks the Query call to be able to recognize scans.
--]]
function vendor.Gatherer:QueryAuctionItems(name, minLevel, maxLevel, invTypeIndex, 
  classIndex, subclassIndex, page, isUsable, qualityIndex, getAll)
  	if (name) then
  		name = string.lower(name)
  	end
	_CheckScan(self, name, tonumber(minLevel), tonumber(maxLevel), invTypeIndex, classIndex, subclassIndex, page, isUsable, qualityIndex, getAll) 
end

--[[
	The ah is closed and the last scan definitely finsihed.
--]]
function vendor.Gatherer:AUCTION_HOUSE_CLOSED()
	log:Debug("AUCTION_HOUSE_CLOSED enter")
--	if (self.scan) then  
--		_RememberScan(self, false)
--	end
	self.scan = nil -- TODO recycle
end

--[[
	Trigger from the scanner, that a scan is about to be started.
--]]
function vendor.Gatherer:AUCTION_MASTER_START_SCAN(evt)
	log:Debug("AUCTION_MASTER_START_SCAN")
	-- ensure that we start a new scan
	self.scan = nil -- TODO recycling
end

--[[
	Trigger from the scanner, that a scan has finished
--]]
function vendor.Gatherer:AUCTION_MASTER_STOP_SCAN(evt, complete)
	log:Debug("AUCTION_MASTER_STOP_SCAN msg [%s]", complete)
	if (complete) then
		_RememberScan(self, complete)
	end
end

--[[
	Returns the below described information for current auctions of the specified item.
    avgBid, avgBuyout, lowerBid, lowerBuyout, upperBid, upperBuyout, numAuctions, numBuyouts
--]]
function vendor.Gatherer:GetCurrentAuctionInfo(itemLink, neutralAh)
	local itemLinkKey = vendor.Items:GetItemLinkKey(itemLink)
	return _GetCurrentAuctionInfo(self, itemLinkKey, neutralAh)	
end

--[[
	Returns the below described all-time information for auctions of the specified item.
    avgBid, avgBuyout, numAuctions, numBuyouts
--]]
function vendor.Gatherer:GetAuctionInfo(itemLink, neutralAh)
	local itemLinkKey = vendor.Items:GetItemLinkKey(itemLink)	
	local info = self.auctionCache[itemLinkKey]
	local _
	if (not info) then
		info = {}
		-- first the current information about the item
		info.avgBid, info.avgBuyout, _, _, _, _, info.numAuctions, info.numBuyouts = _GetCurrentAuctionInfo(self, itemLinkKey, neutralAh)
		
		-- now the all-time information
		local itemInfo = self.itemInfo
		if (vendor.Items:GetItemInfo(itemLinkKey, itemInfo, neutralAh)) then
			-- TODO without mult
			local numValues, avgValue = self.bidAvg:GetInfo(itemInfo.avgBidData)
			if (numValues > 0) then
				info.avgBid = math.floor(((info.avgBid * info.numAuctions) + (numValues * avgValue)) / (numValues + info.numAuctions) + 0.5)
				info.numAuctions = info.numAuctions + numValues
			end
			numValues, avgValue = self.buyoutAvg:GetInfo(itemInfo.avgBuyoutData)
			if (numValues > 0) then
				info.avgBuyout = math.floor(((info.avgBuyout * info.numBuyouts) + (numValues * avgValue)) / (numValues + info.numBuyouts) + 0.5)
				info.numBuyouts = info.numBuyouts + numValues
			end
		end
    	self.auctionCache[itemLinkKey] = info
	end
	return info.avgBid, info.avgBuyout, info.numAuctions, info.numBuyouts
end

--[[
	Cleanup the snapshot from outdated auctions.
--]]
function vendor.Gatherer:CleanupSnapshot()
	local snapshot = self.db.factionrealm.snapshot
	local neutralAh = vendor.AuctionHouse:IsNeutral()
	if (neutralAh) then
		snapshot = self.db.realm.snapshot
	end
	_CleanupSnapshot(self, snapshot, neutralAh)
end

--[[
	Unpacks the information contained in the auction data represantation.
	itemLinkKey, count, minBid, buyout, name, quality, level, minIncrement, bidAmount, 
		highBidder, owner, saleStatus, time, timeLeft
--]]
function vendor.Gatherer:Unpack(data)
	return _Unpack(data)
end

--[[
	Will be called asynchronously from a task and will archive the given scan.
--]]
function vendor.Gatherer:ArchiveScan(scan)
	_ArchiveScan(self, scan)
end
